# This is a version of the resource module from before I decided to remove the
# Library system because of its complexity.
# I have a feeling I will return to that model in the future, so I have kept
# a copy of the old code here.

"""
    This module defines the resource management system.
    
    You may notice that the resource management system resembles the XML DOM,
    not in its nomenclature, but in its mannerisms. This is (mostly)
    intentional.
    
    You may also notice that there are a darned lot of attributes, methods,
    slots, etc. in here for such a brand new API. Well, that's because much of
    it is prospective. I've included everything I think I might ever need. If I
    end up not needing something, I will probably take it out. But I want to
    make sure that I have as complete of a picture as possible before moving on.
    
    The plan is not to implement the complicated library stuff right away.
    I will just focus on the ID-based system for now.
    
    Also note that there is going to be a graphical tool for building complex
    library trees. In much of the planning I did in writing this module I
    assumed that most users would be using that tool.
"""

__all__ = ['Library','BaseResource']

# Holds a reference to the root library. (Created at init time.)
_root = None

# Holds a mapping of all known resource extension sets to all known
# resource type classes.
_type = {}

# Holds a mapping of all resource IDs to the resources themselves.
_resource = {}

def init():
    """ Initializes the resource management system.
        Must be called before using the resource management system
    """
    global _root
    _root = Library()

def register_type(cls):
    """ Registers (or re-registers) a type class. """
    pass # TODO: register_type

def _register(res):
    """ Internal function.
        Registers a resource with the manager.
        You don't need to call this usually because resources will register
        themselves automatically, provided they call the BaseResource
        initializer on themselves.
    """
    pass # TODO: _register

def _unregister(res):
    """ Internal function.
        Unregisters a resource from the resource manager.
        You very rarely want to use this manually because it has high potential
        to totally mess up the internals.
    """
    pass # TODO: _unregister

def get(id):
    """ Returns the resource with the given ID.
        Returns None if there isn't a resource registered witht the ID.
    """
    pass # TODO: get_by_id

def get_root():
    """ Returns a reference to the root library. """
    return _root

class Library(object):
    """ A Library organizes and manages a group resources of varying type. """
    
    __slots__ = (
        '_res',    # Mapping of all resources' names to the resources
                   # themselves. NOTE: A name is NOT necessarily the
                   # resource's ID. It is the resource's alias for the
                   # library.
        '_lib',    # Mapping of all child libs' names to the libs themselves
        '_name',   # Name of the library
        '_parent', # Reference to the parent library
    )  
    
    def __init__(self, name):
        self._res = {}
        self._lib = {}
        self._name = name
        pass
        
    def add_resource(self, res, name=None):
        """ Registers a resource with the library using the given name.
            
            If you do not specify a name, the resource's primary name will be
            used to determine the name for this library. If you do specify a
            name and the resource has no primary name, the name will become
            its primary. 
            
            Raises error.ResourceError if a resource is already registered
            with the given name, or if you don't give a name when resource has
            no primary name.
        """
        pass # TODO: Library.add_resource
    
    def _add_resource(self, res, name=None):
        """ Internal function.
            Called by resources to tell the library they want to be registered
            with it.
        """
        pass # TODO: Library._add_resource
    
    def remove_resource(self, res):
        """ Unregisters a resource from the library.
            
            You may refer to a reference either by its name in the library, its
            ID, or by passing a reference to the resource itself. (The type of
            the value will be checked in that order.)
            
            Attempting to remove a resource which is not part of the library
            will raise error.ResourceError.
        """
        pass # TODO: Library.remove_resoruce
    
    def _remove_resource(self, res):
        """ Internal function.
            Called by resources to tell the library that it no longer wants to
            be registered with it.
        """
        pass # TODO: Library._remove_resource
    
    def stub_resource(self, filename, name=None, type=None):
        """ Makes the library aware of a resource that may be loaded.
            
            Pass the filename of the resource. Returns a reference to the
            resource stub.
            
            This does not actually load a resource's data; it only creates the
            stub.
            
            Note that if a resource requires other resources, those resources
            will NOT be stubbed as well. This is because the dependencies of the
            resource are in the file, and since the file is not being loaded,
            there is no way to know what they are.
            
            You may specify a name to give to the resource for this library. If
            you do not, the name will be determined from the filename.
            
            You can also specify a type to load the resource as. This must be a
            resource type class that has been registered with
            Library.register_type(). If you do not specify a type, the type
            will be determined by the extension of the filename.
        """
        pass # TODO: Library.stub_resource
    
    def stub_resources(self, *res):
        """ Stubs many resources at once. 
            
            Pass as many arguments as there are resources that you want to load.
            Returns a list containing all the stubs.
            
            Each argument should be either a filename string or a tuple or other
            sequence. If it is a sequence, the items should be:
            0: the filename of the resource,
            1: the name of the resource for this library (or None to make it the
               same as the filename), and
            2: the type class of the resource.
            
            Ex: (filename, name, typeCls)
        """
        pass # TODO: Library.stub_resources
    
    def load_resource(self, filename, name=None, type=None):
        """ Loads the file as a new resource of this library.
            
            This works the same as Library.stub(), except that the data is
            actually loaded.
            
            Unlike stub, since the file will be loaded immediately, all
            of the resources required will be loaded as new resources as well,
            unless they have already been loaded.
        """
        pass # TODO: Library.load_resource
    
    def load_resources(self, *res):
        """ Loads many resources at once. 
            
            Works the same as stub_resources() except that the data is actually
            loaded, meaning that dependencies will be loaded as well.
        """
        pass # TODO: Library.load_resources
    
    def load_all_resources(self, type=None):
        """ Loads all resources that have been registered with this library.
            
            Optionally, you may specify a resource type (a resource type class)
            to load only resources of the given type.
        """
        pass # TODO: Library.load_all_resources
    
    def get_resource(self, path, failOnNoLoad=False):
        """ Returns a refererence to the resource with the given path relative
            to the library.
            
            If failOnLoad is true, error.ResourceError will be raised when the
            resource has not yet been loaded. Otherwise, the resource will be
            loaded automatically, along with any dependencies.
        """
        pass # TODO: Library.get_resource
    
    def get_resources(self, type=None):
        """ Returns a dictionary mapping the names of all resources in this
            library to the actual resources.
            
            Optionally you can return a dictionary that only contains resources
            of a certain type by passing a resource type class.
        """
        pass # TODO: Library.get_resources
    
    def add_library(self, lib):
        """ Adds an existing library to this library.
        
            Pass a reference to a library.
        
            You cannot add a library to two parent libraries simultaneously.
            If you attempt to add a library which is already part of another, 
            error.ResourceError is raised.
        """
        pass # TODO: Library.add_library
    
    def remove_library(self, lib):
        """ Removes the given library from this library.
        
            Pass a reference to the library to be removed or else the name of
            the library.
            
            Be aware that removing a library can be slow if it contains many
            resources or resources with many aliases because all aliases of all
            resources must be updated!
            
            error.ResourceError is raised if the library given is not a part
            of this library.
        """
        pass # TODO: Library.remove_library
    
    def create_library(self, name):
        """ Creates a new library within this library with the given name.
        
            Raises error.ResourceError if the name is already in use.
        """
        pass # TODO: Library.create_library
    
    def destroy_library(self, lib):
        """ Completely destroys a child library.
        
            Destroying a library means that all its children are unregistered
            from it, including both its child libraries and resources, and then
            the library is removed from its parent.
            
            Be very careful with this. It has the potential to create "dangling"
            libraries; that is, libraries that don't have parents and are not
            part of any containing trees but still contain children. In
            general, only use it when the library is empty.
        """
        pass # TODO: Library.destroy_library
    
    def destroy_library_recursive(self, lib):
        """ Completely destroys a child library recursively.
        
            This is the same as destroy_library() except that the child
            libraries are destroyed as well, as are their libraries, and so on.
        """
        pass # TODO: Library.
    
    def move(self, lib):
        """ Moves this library from its current location to the new location.
        
            Pass a reference to the new library.
            
            Be aware that moving a library can be slow if it contains many
            resources or resources with many aliases because all aliases of all
            resources must be updated!
        """
        pass # TODO: Library.move

class BaseResource(object):
    """ Base class for all resource types.
        All classes derived from this class must have an attribute called
        `exts` which is a tuple (or other immutable sequence) that contains the
        extensions of file types which may contain data that the type can load.
        
        Example:
        
        class Image(BaseResource):
            ...
            ext = ('png', 'bmp', ...)
    """
    
    __slots__ = (
        '_id',       # Unique keyword ID of the resource
        '_name',     # Primary alias (used for determining aliases for libraries)
        '_path',     # Path to pimrary library
        '_lib',      # Reference to primary library
        '_alias',    # Mapping of all aliases in all libs to the actual libs
        '_filename', # Name of file from which this resource was last saved/loaded
        '_loaded',   # Loaded status (0=not loaded, 1=explicit, 2=implicit)
        '_depend',   # List of the locations or IDs of all dependencies
    )
    
    def __init__(self, id=None, filename=None):
        """ BaseResource initializer.
        
            TODO: What about id and filename...?
            
            This should be called by subclasses.
        """
        pass
    
    def add_depend(self, dep, ignoreMissing=False):
        """ Adds a dependency to the end of the resource's dependencies.
            
            The dependency should be a library location of another resource
            or a resource ID. If it is a location, it must be url relative to
            the root library (indicated by a string starting with a '/') or else
            a url relative to the path of this resource's primary name
            (indicated by a string starting with './'). If you use an ID, simply
            use the unaltered ID (no prefix necessary).            
            
            Attempting to add a dependency on which the resource already depends
            will result in the 'old' dependency to be replaced by the 'new' one,
            which usually doesn't change anything.
            
            If ignoreMissing is true, a dependency which is not actually
            registered with the resource manager will be added to the
            dependencies regardless. However, when the time comes to load the
            resource's data, an exception may occur.
            
            If ignoreMissing is false (default), a missing depenency will
            immediately raise error.ResourceError.
        """
        pass
    
    def remove_depend(self, dep):
        """ Removes the given dependency from this resource.
            Raises error.ResourceError if the resource does not depend on the
            other resource.
        """
        pass
    
    def depends(self, dep):
        """ Returns whether or not this resource depends on the given resource. """
        pass
    
    def get_depend_list(self):
        """ Returns a list of all resources this resource depends on. """
        pass            
    
    def load(self):
        """ Loads the resource data explicitly.
            Does not load data if it is already loaded.
            This is called by Library.load() and Library.get_ref(), but other
            resources depending on this one will use
            BaseResource.load_implicit() instead.
            If this resource depends on other resources it should, if necessary,
            register them with the library and load the data for them
            implicitly.
        """
        pass
    
    def load_implicit(self):
        """ Loads resource data implicitly.
            Does not load data if it is already loaded.
            This is used by other resources to load this resource on demand.
            Has the same function as load() if this resource has already been
            loaded explicitly.
        """
        pass
    
    def unload(self):
        """ Unloads the resource data if it is loaded.
            Also decrements the in-use counter of all resources that were being
            used by this resource.
        """
        pass
    
    def unload_safe(self):
        """ Unloads this resource only if no other resources are using it.
            If a resource is still using it, error.ResourceError() is raised.
        """
        pass
    
    def get_loaded(self):
        """ Returns the loaded status of this resource:
            0 = Not loaded,
            1 = Loaded explicitly,
            2 = Loaded implicitly. (The data of implicitly-loaded resources are
                unloaded automatically when the last resource using it, other
                than this one, unloads its data.)
                (FIXME: Is the above completely accurate?)
        """
        pass
    
    def add_library(self, lib):
        """ Registers this resource with the specified library.
            If a library is already registered with a library, registering it
            with that library again will have no effect.
            Note that a resource should be automatically registered with the MRL
            when it is created.
        """
        pass
    
    def _register(self, lib):
        """ Internal function.
            
            Called by the registrar (a Library instance) to tell the resource
            it is being registered.
        """
        pass
    
    def remove_library(self, lib):
        """ Unregisters the resource from the library specified.
            
            If the resource is not registered with the given library, nothing
            happens.
        """
        pass
    
    def _remove_library(self, lib):
        """ Internal function.
            Called by Libraries to tell them that they are no longer going to
            be registered.
        """
        pass
